#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

#define BLUE          	"\033[0;32;24m"
#define RED             "\033[0;31;24m"
#define DEPBLUE         "\033[0;34;24m"
#define NONE            "\033[0m"

#define CLEAR()         printf("\033[2j")
#define HIGH_LIGHT      printf("\033[7m")
#define UN_HIGH_LIGHT   printf("\033[27m")
#define RESET           printf(NONE)
#define MOVETO(x,y)     printf("\033[%d;%dH",(y),(x))
 
#define HEIGHT 20
#define WIDETH 40

typedef enum MOVE_DIR {UP = -1,DOWN = 1,LEFT = 2,RIGHT = 4}DIR;
enum MOVE_DIR cur_dir = RIGHT;
int ver = 1;
int score = 0;
int flag = 0;
int temp = 1;
int snakelength = 3;
int foodlength = 1;
char choose;

typedef struct snake_date{
	int x;
	int y;
	enum MOVE_DIR dir;
}Snake_date;

typedef struct snake_node{
	struct snake_date date;
	struct snake_node* next;
}sanke,*Psnake;

typedef struct food_date{
	int x;
	int y;
//	int color;
	int time;
//	struct food_node* next;
}food;

typedef struct food_node{
	struct food_date date;
	struct food_node* next;
}fod,*Pfood;

struct food_node *fhead;
struct snake_node *head;

/*int draw_food()							//food chushi dian		
{
	srand(time(NULL));
	food1.x = rand()%38 + 21;
	food1.y = rand()%19 + 6;
}*/

void area()									//youxiqujian
{
	int i;
	MOVETO(20,5);
	printf("┏");
	for(i=0;i<WIDETH-2;i++)
		printf("━");
	printf("┓");
	for(i=1;i<HEIGHT;i++)
	{
		MOVETO(20,5+i);
		printf("┃");
		MOVETO(20+WIDETH-1,5+i);
		printf("┃");
	}
	MOVETO(20,5+HEIGHT);
	printf("┗");
	for(i=0;i<WIDETH-2;i++)
		printf("━");
	printf("┛");
//	MOVETO(food1.x,food1.y);
//	printf("♟");
}

struct foood_node *fcreat_link(int m)
{
	srand(time(NULL));
	struct food_node *p,*ptail;
	int i;
	for(i=0;i<m;i++)
	{
		p = (struct food_node *)malloc(sizeof(struct food_node));
		if(p == NULL)
		{
			free(fhead);
			printf("failure\n");
			return NULL;
		}
		p->date.x = rand()%38 + 21;
		p->date.y = rand()%19 + 6;
		p->date.time = (rand()%3+2)*10;
		if(i == 0)
			fhead = p;
		else
			ptail->next = p;
		ptail = p;
		p->next = NULL;
	}
}

struct snake_node *creat_link(int n)
{
	struct snake_node *p,*ptail;
	int i;
	for(i=0;i<n;i++)
	{
		p = (struct snake_node *)malloc(sizeof(struct snake_node));
		if(p == NULL)
		{
			free(head);
			printf("failure\n");
			return NULL;
		}
		p->date.x = 30+i;
		p->date.y = 15;
		p->date.dir = RIGHT;
		if(i == 0)
			head = p;
		else
			ptail->next = p;
		ptail = p;
		p->next = NULL;
	}
}

void init_game()
{
	FILE *fp;
	unsigned char buf[64];
	int n = 3,m = 1;	
	fp = fopen("snake.dat","r");
	if(fp == NULL)
	{
		creat_link(n);
		fcreat_link(m);
		if(fp)
			fclose(fp);
		return;
	}
	else
	{
		printf("you have a date before,whether continue(y/Y) or restart(n/N):\n");
		while(choose = getchar()){
		if(choose == 'n'|| choose =='N')
		{
			creat_link(n);
			fcreat_link(m);
			if(fp)
				fclose(fp);
			return;
		}
		else if(choose == 'y' || choose =='Y')
		{
			fread(buf,6,1,fp);
			if(strncmp(buf,"SNAKEA",6)!=0)
			{
				creat_link(n);
				fcreat_link(m);
				if(fp)
					fclose(fp);
				return;
			}
			else
			{
				fread(&ver,4,1,fp);
				if(ver == 1)
				{
					int i;
					struct snake_node *p;
					struct snake_date date1;
					struct food_node *q;
					struct food_date date2;	
					fread(&n,4,1,fp);
					snakelength = n;
					fread(&score,4,1,fp);
					creat_link(snakelength);
					p = head;
					for(i=0;i<n;i++)
					{
						fread(&date1,sizeof(struct snake_date),1,fp);
						p->date = date1;
						cur_dir = p->date.dir;
						p = p->next;
					}
					fread(&m,4,1,fp);
					foodlength = m;
					fcreat_link(foodlength);
					q = fhead;
					for(i=0;i<m;i++)
					{
						fread(&date2,sizeof(struct food_date),1,fp);
						q->date = date2;
						q = q->next;
					}
				}
			}
			if(fp)
			fclose(fp);
		return;
		}

	}
	}
}

int add_food()
{
	struct food_node *pnew,*pold,*p = fhead;
	if(score%5 == 0)
	{
		foodlength++;
		pnew = (struct food_node *)malloc(sizeof(struct food_node));
		pnew->date.x = rand()%38 + 21;
		pnew->date.y = rand()%19 + 6;
		p->date.time = (rand()%3+2)*10;
		p = fhead;
		while(p!=NULL)
		{
			pold = p;
			p = p->next;
		}
		pold->next = pnew;
		pnew->next = p;
	}
}

int snake_move()
{
	struct snake_node *pnew,*pold,*p = head,q;
	struct food_node *f;
	int none = 1;
	while(p!= NULL && p->next!=NULL)
		p = p->next;

	if(cur_dir == RIGHT || cur_dir == LEFT)
	{
		q.date.x = p->date.x + (cur_dir-3);
		q.date.y = p->date.y;
	}
	else
	{
		q.date.x = p->date.x;
		q.date.y = p->date.y + cur_dir;
	}

	if(q.date.x > WIDETH+20-2 || q.date.x < 21)
		flag=1;
	else if(q.date.y > HEIGHT+5-1 || q.date.y < 6)
		flag=1;

	p = head;
	while( p->next !=NULL )
	{
			if(q.date.x == p->date.x && q.date.y == p->date.y)
			flag  = 1;
		p = p->next;
	}

	f = fhead;
	while(f != NULL)
	{
	if(q.date.x == f->date.x && q.date.y == f->date.y)
	{
		score++;
		snakelength++;
		none = 0;
		f->date.x = rand()%38 + 21;
		f->date.y = rand()%19 + 6;
		f->date.time = (rand()%3+2)*10;
		p = head;
		while(p != NULL)
		{
			if(f->date.x == p->date.x && f->date.y == p->date.y)
			{
				f->date.x = rand()%38 + 21;
				f->date.y = rand()%19 + 6;
				f->date.time = (rand()%3+2)*10;
				p = head;
			}
			p = p->next;
		}
		add_food();
//		MOVETO(food1.x,food1.y);
//		printf("♟");

		pnew = (struct snake_node *)malloc(sizeof(struct snake_node));
		pnew->date.x = q.date.x;
		pnew->date.y = q.date.y;
		pnew->date.dir = cur_dir;
		p = head;
		while(p!=NULL)
		{
			pold = p;
			p = p->next;
		}
		pold->next = pnew;
		pnew->next = p;
	}
	f = f->next;
}
	if(none == 1)
	{
		p = head;
		while(p!= NULL && p->next!=NULL)
		{
			p->date.x = p->next->date.x;
			p->date.y = p->next->date.y;
			p->date.dir = p->next->date.dir;

			p = p->next;
		}
		if(cur_dir == RIGHT || cur_dir == LEFT)
			p->date.x += (cur_dir-3);
		else
			p->date.y += cur_dir;
		p->date.dir = cur_dir;
	}
}

int creat_food()
{
	struct food_node *p = fhead;
	while(p!=NULL)
	{
		p->date.time = p->date.time - 1;
		if(p->date.time == 0)
		{
			p->date.x = rand()%38 + 21;
			p->date.y = rand()%19 + 6;
			p->date.time = (rand()%3+2)*10;
		}
		p=p->next;
	}
}

int draw_snake(struct snake_node *head)
{
	int x,y;
	struct snake_node *p = head;
	while(p->next!=NULL)
	{
		MOVETO(p->date.x,p->date.y);
		printf("✦");
		p=p->next;
		MOVETO(p->date.x,p->date.y);
		switch(p->date.dir)
		{
			case RIGHT:printf("♚");break;
			case LEFT:printf("♚");break;
			case UP:printf("♚");break;
			case DOWN:printf("♚");break;
		}
		printf("\n");
	}
/*	MOVETO(10,26);
	printf("%d\n",snakelength);*/
}


int draw_food(struct food_node *fhead)
{
	struct food_node *p = fhead;
	while(p != NULL)
	{
		MOVETO(p->date.x,p->date.y);
		printf("♟");
		p = p->next;
		printf("\n");
	}
}

int keyboard()
{
	struct termios oldt,newt;
	int ch;
	int oldf;
	tcgetattr(STDIN_FILENO,&oldt);
	newt = oldt;
	newt.c_lflag &= ~(ICANON | ECHO);
	tcsetattr(STDIN_FILENO,TCSANOW,&newt);
	oldf = fcntl(STDIN_FILENO,F_GETFL,0);
	fcntl(STDIN_FILENO,F_SETFL, oldf | O_NONBLOCK);

	ch =getchar();

	tcsetattr(STDIN_FILENO,TCSANOW,&oldt);
	fcntl(STDIN_FILENO,F_SETFL,&oldf);

	if(ch != EOF)
		return ch;
	else
		return 0;
}

void save_game(struct snake_node *head)
{
	FILE *fp;
	struct snake_node *p;
	struct food_node *q;
	unsigned char buf[64];
	fp = fopen("snake.dat","w");
	if(fp == NULL)
	{
		perror("open write error");
		return;
	}
	fwrite("SNAKEA",6,1,fp);
	fwrite(&ver,4,1,fp);
	fwrite(&snakelength,4,1,fp);
	fwrite(&score,4,1,fp);
	p = head;
	while(p!= NULL)
	{
		fwrite(&p->date,sizeof(struct snake_date),1,fp);
		p = p->next;
	}
	fwrite(&foodlength,4,1,fp);
	q = fhead;
	while(q != NULL)
	{
		fwrite(&q->date,sizeof(struct food_date),1,fp);
		q = q->next;
	}
	fclose(fp);
	fp = fopen("snake.dat","r");
	if(fp == NULL )
	{
		perror("open write");
		return;
	}
	fread(buf,6,1,fp);
	printf("%s\n",buf);
	fread(&ver,4,1,fp);
	printf("%d\n",ver);
	fread(&snakelength,4,1,fp);
	printf("%d\n",snakelength);
	fread(&score,4,1,fp);
	printf("%d\n",score);
	p = head;
	while(p!= NULL)
	{
		fread(&p->date,sizeof(struct snake_date),1,fp);
		printf("%d %d ",p->date.x,p->date.y);
		p = p->next;
	}
	fread(&foodlength,4,1,fp);
	printf("%d\n",foodlength);
	q = fhead;
	while(q!=NULL)
	{
		fread(&q->date,sizeof(struct food_date),1,fp);
		printf("%d %d ",q->date.x,q->date.y);
		q = q->next;
	}
}

int main()
{
	int t = 0,time,n=3;
	char ch;
	char c;
	init_game();

	while(1)
	{
		switch(keyboard())
		{
			case 'w':if(temp) cur_dir = UP;break;
			case 's':if(temp) cur_dir = DOWN;break;
			case 'a':if(temp) cur_dir = LEFT;break;
			case 'd':if(temp) cur_dir = RIGHT;break;
			case 'f':t = 2;time = 5;break;
			case ' ':temp = !temp;break;
			case 'q':save_game(head);return 0;
		}
		area();
		if(temp)
			snake_move(head);
		MOVETO(1,26);
		printf("score :%d",score);
		if(flag)
			return 0;
		creat_food();
		draw_food(fhead);
		draw_snake(head);
		if(time == 0)
		t = 0;
		time--;
		usleep(1000000/(score/5+1+t));
		system("clear");
	}

	return 0;
}